package com.weijianhuawen.wjblog.admin.service.impl;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.weijianhuawen.wjblog.admin.convert.ArticleDetailConvert;
import com.weijianhuawen.wjblog.admin.event.DeleteArticleEvent;
import com.weijianhuawen.wjblog.admin.event.PublishArticleEvent;
import com.weijianhuawen.wjblog.admin.event.UpdateArticleEvent;
import com.weijianhuawen.wjblog.admin.mode.vo.article.*;
import com.weijianhuawen.wjblog.admin.service.AdminArticleService;
import com.weijianhuawen.wjblog.common.domain.dos.*;
import com.weijianhuawen.wjblog.common.domain.mapper.*;
import com.weijianhuawen.wjblog.common.enums.ResponseCodeEnum;
import com.weijianhuawen.wjblog.common.exception.BizException;
import com.weijianhuawen.wjblog.common.utils.PageResponse;
import com.weijianhuawen.wjblog.common.utils.Response;
import io.minio.messages.Tags;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.jni.Local;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @version: java version 8
 * @Author: weijianhuawen
 * @description:
 * @date: 2024-10-24 16:47
 */
@Service
@Slf4j
public class AdminArticleServiceImpl implements AdminArticleService {

    @Resource
    private ArticleMapper articleMapper;
    @Resource
    private ArticleContentMapper articleContentMapper;
    @Resource
    private CategoryMapper categoryMapper;
    @Resource
    private TagMapper tagMapper;
    @Resource
    private ArticleTagRelMapper articleTagRelMapper;
    @Resource
    private ArticleCategoryRelMapper articleCategoryRelMapper;
    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;
    @Value("${image.default.url}")
    private String defaultImageUrl;
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Response publishArticle(PublishArticleReqVo vo) {
        //1 文章相关信息处理
        // 将请求vo中有关文章信息字段提取，并封装为do
        ArticleDo articleDo = ArticleDo
                .builder()
                .title(vo.getTitle())
                .cover(vo.getCover())
                .summary(vo.getSummary())
                .createTime(LocalDateTime.now())
                .updateTime(LocalDateTime.now())
                .build();
        // 将文章相关信息插入
        articleMapper.insert(articleDo);

        // 获取文章id
        Long articleId = articleDo.getId();
        //2 文章内容处理
        // 将请求中有关内容的信息字段提取
        ArticleContentDo articleContentDo = ArticleContentDo
                .builder()
                .content(vo.getContent())
                .articleId(articleId)
                .build();

        articleContentMapper.insert(articleContentDo);
        //3 文章分类处理
        // 查询分类是否存在
        CategoryDo categoryDo = categoryMapper.selectById(vo.getCategoryId());
        if (Objects.isNull(categoryDo)) {
            log.warn("所选分类id不存在, category_id: {}", vo.getCategoryId());
            throw new BizException(ResponseCodeEnum.NOT_EXIST_CATEGORY);
        }
        // 从请求中提取相关字段
        ArticleCategoryRelDo articleCategoryRelDo = ArticleCategoryRelDo
                .builder()
                .categoryId(vo.getCategoryId())
                .articleId(articleId)
                .build();

        articleCategoryRelMapper.insert(articleCategoryRelDo);
        //4 文章标签处理
        // 将所请求的标签保存
        List<String> publishTags = vo.getTags();
        //保存标签
        insertTags(articleId, publishTags);
        // 发布文章事件生效
        applicationEventPublisher.publishEvent(new PublishArticleEvent(this, articleId));
        return Response.success(articleId);
    }

    /**
     * 删除文章 包括文章详情 文章内容 标签关联 分类关联 所有删除要么同成功 要么同失败
     * @param vo
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Response deleteArticle(DeleteArticleReqVo vo) {
        // 获取需要删除文章的id
        Long id = vo.getId();

        // 删除文章详情
        articleMapper.deleteArticleById(id);
        // 删除文章内容
        articleContentMapper.deleteArticleContentByArticleId(id);
        // 删除文章分类关联
        Long categoryId = articleCategoryRelMapper.selectByArticleId(id).getCategoryId();
        articleCategoryRelMapper.deleteArticleCategoryOfArticleByArticleId(id);
        // 删除文章标签关联
        List<ArticleTagRelDo> articleTagRelDos = articleTagRelMapper.selectByArticleId(id);
        List<Long> tagIds = Lists.newArrayList();
        if (!CollectionUtils.isEmpty(articleTagRelDos))
            tagIds = articleTagRelDos.stream().map(ArticleTagRelDo::getTagId).collect(Collectors.toList());
        articleTagRelMapper.deleteTagsOfArticleByArticleId(id);

        // 发布删除文章事件
        applicationEventPublisher.publishEvent(new DeleteArticleEvent(this, id, categoryId, tagIds));
        return Response.success();
    }
    // 分页查询
    @Override
    public Response findArticlePageList(FindArticlePageListReqVo vo) {
        // 获取查询条件
        Long current = vo.getCurrent();
        Long size = vo.getSize();
        String title = vo.getTitle();
        LocalDate startDate = vo.getStartDate();
        LocalDate endDate = vo.getEndDate();
        Integer type = vo.getType();

        // 执行分页查询
        Page<ArticleDo> articleDoPage = articleMapper.selectPageList(current, size, title, startDate, endDate, type);
        List<ArticleDo> articleDos = articleDoPage.getRecords();

        // 将do 转 vo
        List<FindArticlePageListRspVo> vos = null;

        if (!CollectionUtils.isEmpty(articleDos)) {
            vos = articleDos.stream()
                    .map(articleDo -> FindArticlePageListRspVo
                            .builder()
                            .id(articleDo.getId())
                            .title(articleDo.getTitle())
                            .cover(articleDo.getCover())
                            .createTime(articleDo.getCreateTime())
                            .isTop(articleDo.getWeight() > 0)
                            .build())
                    .collect(Collectors.toList());
        }
        if (vos == null) vos = new ArrayList<>();
        return PageResponse.success(articleDoPage, vos);
    }

    // 查询文章详情
    @Override
    public Response findArticleDetail(FindArticleDetailReqVo vo) {
        // 获取文章id
        Long id = vo.getId();
        ArticleDo articleDo = articleMapper.selectByArticleId(id);

        if (Objects.isNull(articleDo)) {
            log.warn("【文章详细信息查询服务】所查询的文章id不存在，文章id：{}", id);
            throw new BizException(ResponseCodeEnum.NOT_EXIST_ARTICLE_ERROR);
        }

        ArticleContentDo articleContentDo = articleContentMapper.selectByArticleId(id);

        // 分类
        ArticleCategoryRelDo articleCategoryRelDo = articleCategoryRelMapper.selectByArticleId(id);
        // 标签
        List<ArticleTagRelDo> articleTagRelDos = articleTagRelMapper.selectByArticleId(id);
        // 对应tag id集合
        List<Long> tagIds = articleTagRelDos.stream().map(ArticleTagRelDo::getTagId).collect(Collectors.toList());

        //将do转vo
        FindArticleDetailRspVo findArticleDetailRspVo = ArticleDetailConvert.INSTANCE.convertDoToVo(articleDo);

        findArticleDetailRspVo.setContent(articleContentDo.getContent());
        findArticleDetailRspVo.setCategoryId(articleCategoryRelDo.getCategoryId());
        findArticleDetailRspVo.setTagIds(tagIds);

        return Response.success(findArticleDetailRspVo);
    }
    // 修改文章
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Response updateArticle(UpdateArticleReqVo vo) {
        Long articleId = vo.getId();
        // 获取文章相关信息
        ArticleDo articleDo = ArticleDo.builder()
                .id(vo.getId())
                .title(vo.getTitle())
                .summary(vo.getSummary())
                .cover(vo.getCover())
                .updateTime(LocalDateTime.now())
                .build();

        // 更新文章信息
        int count = articleMapper.updateById(articleDo);

        if (count == 0) {
            // 文章不存在
            log.warn("【文章更新服务】目标文章不存在");
            throw new BizException(ResponseCodeEnum.NOT_EXIST_ARTICLE_ERROR);
        }

        // 更新文章内容
        ArticleContentDo articleContentDo = ArticleContentDo.builder()
                .articleId(articleId)
                .content(vo.getContent())
                .build();

        count = articleContentMapper.updateByArticleId(articleContentDo);
        if (count == 0) {
            // 对应内容不存在，但文章存在，则插入数据
            articleContentMapper.insert(articleContentDo);
        }

        // 更新分类
        // 获取分类id 并查询分类是否存在
        Long categoryId = vo.getCategoryId();
        CategoryDo categoryDo = categoryMapper.selectById(categoryId);
        if (Objects.isNull(categoryDo)) {
            // 对应分类不存在
            log.warn("【文章更新服务】 传入的分类id对应分类不存在");
            throw new BizException(ResponseCodeEnum.NOT_EXIST_CATEGORY);
        }

        // 分类存在则更新分类文章关联表
        ArticleCategoryRelDo articleCategoryRelDo = ArticleCategoryRelDo.builder()
                .articleId(articleId)
                .categoryId(categoryId)
                .build();

        // 更新分类与文章id联系
        count = articleCategoryRelMapper.updateByArticleId(articleCategoryRelDo);
        if (count == 0) {
            // 不存在对应分类与文章关联，则插入关联
            articleCategoryRelMapper.insert(articleCategoryRelDo);
        }

        // 更新标签关联
        // 删除文章关联的标签
        articleTagRelMapper.deleteTagsOfArticleByArticleId(articleId);
        List<String> tags = vo.getTags();

        // 插入新文章关联标签
        insertTags(articleId, tags);
        // 发布修改文章事件
        applicationEventPublisher.publishEvent(new UpdateArticleEvent(this, articleId));
        return Response.success();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Response autoSaveArticle(AutoSaveArticleReqVo vo) {
        // 检查相关字段
        if (vo.getTitle() == null || "".equals(vo.getTitle())) {
            // 标题为空 设置默认标题
            StringBuilder randomTitleBuilder = new StringBuilder("未命名文章");
            UUID uuid = UUID.randomUUID();
            String[] uuids = uuid.toString().split("-");
            for (String s : uuids) {
                randomTitleBuilder.append(s);
            }
            vo.setTitle(randomTitleBuilder.toString());
        }

        if (vo.getSummary() == null || "".equals(vo.getSummary())) {
            // 摘要为空 设置默认摘要
            String defaultSummary = "未见花闻 若见远方 文章摘要";
            vo.setSummary(defaultSummary);
        }

        if (vo.getCover() == null || "".equals(vo.getCover())) {
            String defaultImgUrl = defaultImageUrl;
            vo.setCover(defaultImgUrl);
        }

        if (vo.getCategoryId() == null || vo.getCategoryId() <= 0) {
            // 设置默认分类
            String categoryName = "默认分类";
            // 查询数据库 判断是否存在默认分类
            CategoryDo categoryDo = categoryMapper.selectByName(categoryName);
            if (Objects.isNull(categoryDo)) {
                // 插入默认分类
                CategoryDo newCategory = CategoryDo.builder().name(categoryName).build();
                categoryMapper.insert(newCategory);
                categoryDo = categoryMapper.selectByName(categoryName);
            }

            vo.setCategoryId(categoryDo.getId());
        }

        if (CollectionUtils.isEmpty(vo.getTags())) {
            // 设置默认标签
            String tagName = "默认标签";
            List<String> tags = Lists.newArrayList();
            tags.add(tagName);
            vo.setTags(tags);
        }
        // 转换vo
        PublishArticleReqVo publishArticleReqVo = PublishArticleReqVo
                .builder()
                .tags(vo.getTags())
                .title(vo.getTitle())
                .summary(vo.getSummary())
                .cover(vo.getCover())
                .content(vo.getContent())
                .categoryId(vo.getCategoryId())
                .build();
        UpdateArticleReqVo updateArticleReqVo = UpdateArticleReqVo
                .builder()
                .id(vo.getId())
                .tags(vo.getTags())
                .title(vo.getTitle())
                .summary(vo.getSummary())
                .cover(vo.getCover())
                .content(vo.getContent())
                .categoryId(vo.getCategoryId())
                .build();
        // 检查id 如果id为空 则启用发布文章服务 id不为空则启用修改文章服务
        // 查询文章id是否有效
        ArticleDo articleDo = articleMapper.selectByArticleId(vo.getId());
        if (vo.getId() == null || vo.getId() <= 0 || Objects.isNull(articleDo)) {
            Response pubResponse = this.publishArticle(publishArticleReqVo);
            // 自动保存，则拦截请求结果 成功返回文章详情响应 否则返回错误原响应
            if (pubResponse.getSuccess()) {
                // 查询文章详情作为响应
                Long id = (Long) pubResponse.getData();
                FindArticleDetailReqVo findArticleDetailReqVo = FindArticleDetailReqVo
                        .builder()
                        .id(id)
                        .build();
                AutoSaveArticleRspVo autoSaveArticleRspVo = AutoSaveArticleRspVo
                        .builder()
                        .detail((FindArticleDetailRspVo) this.findArticleDetail(findArticleDetailReqVo).getData())
                        .build();
                return Response.success(autoSaveArticleRspVo);
            } else {
                return pubResponse;
            }
        } else {
            Response updateResponse =  this.updateArticle(updateArticleReqVo); // 有效则修改文章
            if (updateResponse.getSuccess()) {
                // 查询文章详情作为响应
                Long id = vo.getId();
                FindArticleDetailReqVo findArticleDetailReqVo = FindArticleDetailReqVo
                        .builder()
                        .id(id)
                        .build();
                AutoSaveArticleRspVo autoSaveArticleRspVo = AutoSaveArticleRspVo
                        .builder()
                        .detail((FindArticleDetailRspVo) this.findArticleDetail(findArticleDetailReqVo).getData())
                        .build();
                return Response.success(autoSaveArticleRspVo);
            } else {
                return updateResponse;
            }
        }
    }

    @Override
    public Response setArticleTop(UpdateArticleIsTopReqVo vo) {
        // 获取文章id
        Long articleId = vo.getId();
        // 查询文章是否存在
        ArticleDo ado = articleMapper.selectByArticleId(articleId);
        if (Objects.isNull(ado)) {
            log.error("【文章置顶】 文章id对应文章不存在！");
            throw new BizException(ResponseCodeEnum.NOT_EXIST_ARTICLE_ERROR);
        }
        // 获取置顶详情
        Boolean isTop = vo.getIsTop();
        // 默认设置权重为0， 即未置顶状态
        Integer weight = 0;
        if (isTop) {
            // 查找数据库中权重最大的文章，并获取其权重值
            ArticleDo articleDo = articleMapper.selectArticleByMaxWeight();
            // 设置为最大文章权重+1的权重值
            if (Objects.nonNull(articleDo)) weight = articleDo.getWeight() + 1;
        }

        // 设置文章权重
        articleMapper.updateById(ArticleDo.builder()
                .id(articleId)
                .weight(weight)
                .build());
        return Response.success();
    }

    private void insertTags(Long articleId, List<String> tags) {
        // 筛选提交的tag
        // 数据库不存在的tag
        List<String> notExistTags = null;
        // 数据库存在的tag
        List<String> existTags = null;

        // 查询出所有标签
        List<TagDo> tagDoList = tagMapper.selectList(null);

        if (CollectionUtils.isEmpty(tagDoList)) {
            // 如果不存在任何标签则将不存在tag列表填满请求所传tags
            notExistTags = tags;
        } else {
            // 获取所有的id
            List<String> tagIds = tagDoList.stream().map(tagDo -> String.valueOf(tagDo.getId())).collect(Collectors.toList());

            // 筛选出包含id的tag标签id
            existTags = tags.stream().filter(tagIds::contains).collect(Collectors.toList());
            notExistTags = tags.stream().filter(tag -> !tagIds.contains(tag)).collect(Collectors.toList());

            // 提交的标签名，数据库已存在情况处理 如传入java 表中存在Java标签 则转换为Java标签
            Map<String, Long> tagNameIdMap = tagDoList.stream().collect(Collectors.toMap(tagDo -> tagDo.getName().toLowerCase(), TagDo::getId));

            // 使用迭代器安全删除 提交转小写后 与数据库转小写后相同的标签， 并将数据库当中的标签存入existList 删除notexistList对应的数据
            Iterator<String> stringIterator = notExistTags.iterator();
            while (stringIterator.hasNext()) {
                String notExistTag = stringIterator.next();
                if (tagNameIdMap.containsKey(notExistTag.toLowerCase())) {
                    // 包含忽略大小写的标签
                    // 删除对应的标签
                    stringIterator.remove();
                    // 将对应的标签添加到已存在的list中
                    existTags.add(String.valueOf(tagNameIdMap.get(notExistTag.toLowerCase())));
                }
            }
        }
        // 将数据库已存在的标签批量管理文章id，并保存到数据库
        if (!CollectionUtils.isEmpty(existTags)) {
            List<ArticleTagRelDo> articleTagRelDos = Lists.newArrayList();

            existTags.forEach(tagId -> {
                ArticleTagRelDo articleTagRelDo = ArticleTagRelDo.builder()
                        .tagId(Long.valueOf(tagId))
                        .articleId(articleId)
                        .build();
                articleTagRelDos.add(articleTagRelDo);
            });
            // 批量插入
            articleTagRelMapper.insertBatchSomeColumn(articleTagRelDos);
        }
        // 将已提交，不存在表中的标签批量保存到数据库
        if (!CollectionUtils.isEmpty(notExistTags)) {
            // 首先将标签入库 生成id 后 再将id与文章id绑定
            List<ArticleTagRelDo> articleTagRelDos = Lists.newArrayList();
            notExistTags.forEach(tagName -> {
                TagDo tagDo = TagDo.builder()
                        .name(tagName)
                        .createTime(LocalDateTime.now())
                        .updateTime(LocalDateTime.now())
                        .build();

                // 存入标签
                tagMapper.insert(tagDo);
                // 拿到保存标签的id
                Long tagId = tagDo.getId();

                // 绑定文章与标签的关系
                ArticleTagRelDo articleTagRelDo = ArticleTagRelDo.builder()
                        .articleId(articleId)
                        .tagId(tagId)
                        .build();

                articleTagRelDos.add(articleTagRelDo);
            });
            // 批量插入
            articleTagRelMapper.insertBatchSomeColumn(articleTagRelDos);
        }
    }
}
